/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * tameshi
 * Copyright (C) Massimiliano Maniscalco 2012 <massi.neptune@yahoo.com>
 * 
tameshi is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * tameshi is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _TAG_TEXT_H_
#define _TAG_TEXT_H_

#include <gtkmm.h>
#include <iostream>

#define BOLD_START			0
#define BOLD_END			1
#define UNDERLINE_START		2
#define UNDERLINE_END		3
#define SUPERSCRIPT_START   4
#define SUPERSCRIPT_END		5
#define HIGHLIGHT_START		6
#define HIGHLIGHT_END		7

using namespace std;

class TagText
{
public:
	static inline Glib::ustring get_string_with_entity_references ( const Glib::ustring &str );
	
	inline TagText() : _stringa ( "" ), _tags ( list<Glib::ustring::size_type> () )
	{}
	inline TagText ( const TagText &tt ) : _stringa ( tt._stringa ), _tags ( list<Glib::ustring::size_type> () )
	{
		set_tags ( tt._tags );
	}
	inline TagText ( const Glib::ustring &str ) : _stringa ( str ), _tags ( list<Glib::ustring::size_type> () )
	{}
	inline ~TagText () {}
	inline TagText& operator=( const TagText &tt );
	inline bool operator<( const TagText &tt ) const;
	inline bool operator==( const TagText &tt ) const;
	inline void set_stringa ( const Glib::ustring &str ) { _stringa.assign ( str ); }
	inline void set_stringa ( const char *cstr );
	inline void append_stringa ( const Glib::ustring &str ) { _stringa.append ( str ); }
	inline Glib::ustring get_stringa ( void ) const { return _stringa; }
	inline void set_testo ( Glib::RefPtr<Gtk::TextBuffer> txtbfr );
	inline void get_testo ( Glib::RefPtr<Gtk::TextBuffer> txtbfr ) const;
	inline void set_tags ( const list<Glib::ustring::size_type> &ttl );
	inline void cleanup ( void );
	inline Glib::ustring get_tag_text_xml ( void ) const;

protected:

private:
	static const unsigned char _numeroTags = 4;
	Glib::ustring _stringa;
	list<Glib::ustring::size_type> _tags;

};

inline Glib::ustring TagText::
get_string_with_entity_references ( const Glib::ustring &str )
{
	Glib::ustring correct = str;
	Glib::ustring::size_type pos = 0;
	Glib::ustring::size_type end = correct.size();
	try
	{
		while ( pos < end )
		{
			Glib::ustring::value_type crt = correct[pos];
			switch ( crt )
			{
				case '&':
					correct.replace ( pos, 1, Glib::ustring( "&amp;" ) );
					pos += 4;
					break;
				case '<':
					correct.replace ( pos, 1, Glib::ustring( "&lt;" ) );
					pos += 3;
					break;
				case '>':
					correct.replace ( pos, 1, Glib::ustring( "&gt;" ) );
					pos += 3;
					break;
				case '\'':
					correct.replace ( pos, 1, Glib::ustring( "&apos;" ) );
					pos += 5;
					break;
				case '\"':
					correct.replace ( pos, 1, Glib::ustring( "&quot;" ) );
					pos += 5;
					break;
			}
			pos++;
		}
	}
	catch(const std::exception& ex)
	{
		cerr << "standard exception: " << ex.what() << endl;
		cerr << "TagText:: " << str.raw() << endl;
	}
	return correct;
}

inline TagText& TagText::
operator=( const TagText &tt )
{
	if ( this != &tt )
	{
		_stringa.assign ( tt._stringa );
		set_tags ( tt._tags );
	}
}

inline bool TagText::
operator<( const TagText &tt ) const
{
	if ( _stringa.raw() < tt._stringa.raw() )
		return true;
	else
		return false;
}

inline bool TagText::
operator==( const TagText &tt ) const
{
	if ( _stringa.raw() == tt._stringa.raw () )
		return true;
	else
		return false;
}

inline void TagText::
set_stringa ( const char *cstr )
{
	if ( cstr )
		_stringa.assign ( cstr );
}

inline void TagText::
set_testo ( Glib::RefPtr<Gtk::TextBuffer> txtbfr )
{
	_tags.clear();
	_stringa.assign ( txtbfr->get_text () );
	Glib::ustring::size_type itStr = 0;
	bool bold = false;
	bool under = false;
	bool super = false;
	bool high = false;
	Gtk::TextBuffer::iterator iter = txtbfr->begin();
	Glib::RefPtr<Gtk::TextBuffer::TagTable> tagTable = txtbfr->get_tag_table ();
	Glib::RefPtr<Gtk::TextTag> boldTag = tagTable->lookup ( "bold" );
	Glib::RefPtr<Gtk::TextTag> underlineTag = tagTable->lookup ( "underline" );
	Glib::RefPtr<Gtk::TextTag> superscriptTag = tagTable->lookup ( "superscript" );
	Glib::RefPtr<Gtk::TextTag> highlightTag = tagTable->lookup ( "highlight" );
	while ( !iter.is_end () )
	{
		if ( iter.has_tag ( boldTag ) )
		{
			if ( !bold )
			{
				bold = true;
				_tags.push_back ( BOLD_START );
				_tags.push_back ( itStr );
			}
		}
		else
		{
			if ( bold )
			{
				bold = false;
				_tags.push_back ( BOLD_END );
				_tags.push_back ( itStr );
			}
		}
		if ( iter.has_tag ( underlineTag ) )
		{
			if ( !under )
			{
				under = true;
				_tags.push_back ( UNDERLINE_START );
				_tags.push_back ( itStr );
			}
		}
		else
		{
			if ( under )
			{
				under = false;
				_tags.push_back ( UNDERLINE_END );
				_tags.push_back ( itStr );
			}
		}
		if ( iter.has_tag ( superscriptTag ) )
		{
			if ( !super )
			{
				super = true;
				_tags.push_back ( SUPERSCRIPT_START );
				_tags.push_back ( itStr );
			}
		}
		else
		{
			if ( super )
			{
				super = false;
				_tags.push_back ( SUPERSCRIPT_END );
				_tags.push_back ( itStr );
			}
		}
		if ( iter.has_tag ( highlightTag ) )
		{
			if ( !high )
			{
				high = true;
				_tags.push_back ( HIGHLIGHT_START );
				_tags.push_back ( itStr );
			}
		}
		else
		{
			if ( high )
			{
				high = false;
				_tags.push_back ( HIGHLIGHT_END );
				_tags.push_back ( itStr );
			}
		}
		iter.forward_char ();
		itStr++;
	}
}

inline void TagText::
get_testo ( Glib::RefPtr<Gtk::TextBuffer> txtbfr ) const
{
	txtbfr->set_text ( _stringa );
	if ( _tags.size() > 0 )
	{
		Glib::ustring::size_type bold_start = Glib::ustring::npos;
		Glib::ustring::size_type under_start = Glib::ustring::npos;
		Glib::ustring::size_type super_start = Glib::ustring::npos;
		Glib::ustring::size_type high_start = Glib::ustring::npos;
		list<Glib::ustring::size_type>::const_iterator iter = _tags.begin();
		list<Glib::ustring::size_type>::const_iterator end = _tags.end();
		while ( iter != end )
		{
			Glib::ustring::size_type codice = *iter;
			iter++;
			if ( iter == end )
				break;
			Glib::ustring::size_type posizione = *iter;
			switch ( codice )
			{
				case BOLD_START:
					bold_start = posizione;
					break;
				case BOLD_END:
					txtbfr->apply_tag_by_name ( "bold", txtbfr->get_iter_at_offset ( bold_start ), txtbfr->get_iter_at_offset ( posizione ) );
					bold_start = Glib::ustring::npos;
					break;
				case UNDERLINE_START:
					under_start = posizione;
					break;
				case UNDERLINE_END:
					txtbfr->apply_tag_by_name ( "underline", txtbfr->get_iter_at_offset ( under_start ), txtbfr->get_iter_at_offset ( posizione ) );
					under_start = Glib::ustring::npos;
					break;
				case SUPERSCRIPT_START:
					super_start = posizione;
					break;
				case SUPERSCRIPT_END:
					txtbfr->apply_tag_by_name ( "superscript", txtbfr->get_iter_at_offset ( super_start ), txtbfr->get_iter_at_offset ( posizione ) );
					super_start = Glib::ustring::npos;
					break;
				case HIGHLIGHT_START:
					high_start = posizione;
					break;
				case HIGHLIGHT_END:
					txtbfr->apply_tag_by_name ( "highlight", txtbfr->get_iter_at_offset ( high_start ), txtbfr->get_iter_at_offset ( posizione ) );
					high_start = Glib::ustring::npos;
					break;
			}
			iter++;
		}
		if ( bold_start != Glib::ustring::npos )
			txtbfr->apply_tag_by_name ( "bold", txtbfr->get_iter_at_offset ( bold_start ), txtbfr->end() );
		if ( under_start != Glib::ustring::npos )
			txtbfr->apply_tag_by_name ( "underline", txtbfr->get_iter_at_offset ( under_start ), txtbfr->end() );
		if ( super_start != Glib::ustring::npos )
			txtbfr->apply_tag_by_name ( "superscript", txtbfr->get_iter_at_offset ( super_start ), txtbfr->end() );
		if ( high_start != Glib::ustring::npos )
			txtbfr->apply_tag_by_name ( "highlight", txtbfr->get_iter_at_offset ( high_start ), txtbfr->end() );
	}
}

inline void TagText::
set_tags ( const list<Glib::ustring::size_type> &ttl )
{
	list<Glib::ustring::size_type>::const_iterator iter = ttl.begin ();
	list<Glib::ustring::size_type>::const_iterator end = ttl.end ();
	_tags.clear();
	while ( iter != end )
	{
		Glib::ustring::size_type val = *iter;
		_tags.push_back ( val );
		iter++;
	}
}

inline void TagText::
cleanup ( void )
{
	_stringa.assign ( "" );
	_tags.clear();
}

inline Glib::ustring TagText::
get_tag_text_xml ( void ) const
{
	Glib::ustring str;
	str.append ( Glib::ustring ( "\t\t\t<locutio>" + get_string_with_entity_references ( _stringa ) + "</locutio>\n" ) );
	if ( _tags.size() > 0 )
	{
		str.append ( Glib::ustring ( "\t\t\t<tag>\n" ) );
		list<Glib::ustring::size_type>::const_iterator iter = _tags.begin ();
		list<Glib::ustring::size_type>::const_iterator end = _tags.end ();
		while ( iter != end )
		{
			str.append ( Glib::ustring ( "\t\t\t\t<tag_genus>" ) + Glib::ustring::format ( *iter ) + Glib::ustring ( "</tag_genus>\n" ) );
			iter++;
			if ( iter != end )
				str.append ( Glib::ustring ( "\t\t\t\t<locus>" + Glib::ustring::format ( *iter ) + "</locus>\n" ) );
			else
				break;
			iter++;
		}
		str.append ( Glib::ustring ( "\t\t\t</tag>\n" ) );
	}
	return str;
}

#endif // _TAG_TEXT_H_
